#include "parsers.h"
#include <iostream>

namespace Thinder
{

using std::string;
using std::vector;
using namespace ParserCombinators;

string parseStringLiteral(string &in)
{
    string str = "";
    consume(in, "\"");
    size_t found = 0;
    while ((found = in.find("\"", found)))
    {
        if (in.at(found - 1) != '\\')
            break;
        ++found;
    }    
    str = in.substr(0, found);
    in.erase(0, found + 1);
    if (str[0] == '\\' && str[1] == '"')
        str = "\"" + str.substr(2, str.length() - 4) + "\"";
    
    return str;
}

ParseTree::Identifier parseIdentifier(string &in) 
{
    ParseTree::Identifier ident;
    consume(in, "(");
    ident.identifier = parseStringLiteral(in);
    consume(in, ",AlexPn ");
    ident.ch = parseInt(in);
    //consume(in, " ");
    ident.line = parseInt(in);
    //consume(in, " ");
    ident.col = parseInt(in);
    consume(in, ")");
    return ident;
}

ParseTree::Name parseName(string &in) 
{
    //ParseTree::Name name;
    return parseList(in, parseIdentifier);
    
}

ParseTree::Modifier parseModifier(string &in)
{
    if (optionalConsume(in, "Public")) 
        return ParseTree::Public;
    if (optionalConsume(in, "Protected")) 
        return ParseTree::Protected;
    if (optionalConsume(in, "Private")) 
        return ParseTree::Private;
    if (optionalConsume(in, "Static")) 
        return ParseTree::Static;
    if (optionalConsume(in, "Abstract")) 
        return ParseTree::Abstract;
    if (optionalConsume(in, "Final")) 
        return ParseTree::Final;
        
    // should not happen
    return ParseTree::Public;
}

ParseTree::Operator parseOperator(string &in)
{
    ParseTree::Operator op;
    
    consume(in, "Operator");
    op.op = parseStringLiteral(in);
    
    return op;
}

ParseTree::FormPar parseFormPar(string &in)
{
    ParseTree::FormPar formPar;
    consume(in, "FormPar (");
    formPar.type = parseType(in);
    consume (in, ")");
    formPar.identifier = parseIdentifier(in);
    
    return formPar;
}

ParseTree::Literal parseLiteral(string &in)
{
    ParseTree::Literal literal;
    
    if (optionalConsume(in, "LiteralInt")) 
    {
        literal.literalType = ParseTree::Literal::Int;
    } else if (optionalConsume(in, "LiteralFloat")) 
    {
        literal.literalType = ParseTree::Literal::Float;
    } else if (optionalConsume(in, "LiteralString")) 
    {
        literal.literalType = ParseTree::Literal::String;
    } else if (optionalConsume(in, "LiteralChar")) 
    {
        literal.literalType = ParseTree::Literal::Char;
    } 
    
    literal.literal = parseStringLiteral(in);
    
    return literal;
}

ParseTree::PrimaryExpression parsePrimaryExpression(string &in)
{
    ParseTree::PrimaryExpression pe;

    //consume(in, "(");
    if (optionalConsume(in, "PrimaryLiteral")) 
    {
        pe.peType = ParseTree::PrimaryExpression::Literal;
        consume(in, "(");
        pe.literal = new ParseTree::Literal(parseLiteral(in));
        consume(in, ")");
    } else if (optionalConsume(in, "PrimaryIdent")) 
    {
        pe.peType = ParseTree::PrimaryExpression::Ident;
        pe.identifier = new ParseTree::Identifier(parseIdentifier(in));
    } else if (optionalConsume(in, "PrimaryExpr")) 
    {
        pe.peType = ParseTree::PrimaryExpression::Expr;
        consume(in, "(");
        pe.expression = new ParseTree::Expression(parseExpression(in));
        consume(in, ")");
    } else if (optionalConsume(in, "PrimaryCast")) 
    {
        pe.peType = ParseTree::PrimaryExpression::Cast;
        consume(in, "(");
        pe.type = new ParseTree::Type(parseType(in));
        consume(in, ")");
        consume(in, "(");
        pe.primary = new ParseTree::Primary(parsePrimary(in));
        consume(in, ")");
    } else if (optionalConsume(in, "PrimaryNew")) 
    {
        pe.peType = ParseTree::PrimaryExpression::New;
        pe.name = parseName(in);
        pe.arguments = parseList(in, parseExpression);
    }

    return pe;
}

ParseTree::PrimaryPost parsePrimaryPost(string &in)
{
    ParseTree::PrimaryPost pp;

    if (optionalConsume(in, "(PrimaryPostIdent")) 
    {
        pp.ppType = ParseTree::PrimaryPost::Ident;
        pp.identifier = new ParseTree::Identifier(parseIdentifier(in));
        pp.next = new ParseTree::PrimaryPost(parsePrimaryPost(in));
        
        consume(in, ")");
    } else if (optionalConsume(in, "(PrimaryPostArray")) 
    {
        pp.ppType = ParseTree::PrimaryPost::Array;
        consume(in, "(");
        pp.arrayExpr = new ParseTree::Expression(parseExpression(in));
        consume(in, ")");
        pp.next = new ParseTree::PrimaryPost(parsePrimaryPost(in));
        
        consume(in, ")");
    } else if (optionalConsume(in, "(PrimaryPostFunc")) 
    {
        pp.ppType = ParseTree::PrimaryPost::Func;
        pp.funcArguments = parseList(in, parseExpression);
        pp.next = new ParseTree::PrimaryPost(parsePrimaryPost(in));
        
        consume(in, ")");
    } else if (optionalConsume(in, "PrimaryPostEmpty")) 
    {
        pp.ppType = ParseTree::PrimaryPost::Empty;
        pp.next = NULL;
    }

    return pp;
}


ParseTree::Primary parsePrimary(string &in)
{
    ParseTree::Primary primary;
    
    consume(in, "Primary ");
    consume(in, "(");
    primary.primaryExpression = parsePrimaryExpression(in);
    consume(in, ")");
    primary.primaryPost = parsePrimaryPost(in);
    
    return primary;
}


ParseTree::Unary parseUnary(string &in)
{
    ParseTree::Unary unary;
    
    consume(in, "Unary ");
    unary.prefix = parseList(in, parseOperator);
    consume(in, "(");
    unary.primary = parsePrimary(in);
    consume(in, ")");
    unary.postfix = parseList(in, parseOperator);
    
    return unary;
}

ParseTree::IdentRest parseIdentRest(string &in)
{
    ParseTree::IdentRest ir;
    if (optionalConsume(in, "IRIdent ")) 
    {
        ir.identRestType = ParseTree::IdentRest::Ident;
        ir.identifier = new ParseTree::Identifier(parseIdentifier(in));
    } else if (optionalConsume(in, "IRArray ")) 
    {
        ir.identRestType = ParseTree::IdentRest::Array;
        consume(in, "(");
        ir.arrayExpr = new ParseTree::Expression(parseExpression(in));
        consume(in, ")");
    } else if (optionalConsume(in, "IRFunc ")) 
    {
        ir.identRestType = ParseTree::IdentRest::Func;
        //consume(in, "(");
        ir.funcArguments = parseList(in, parseExpression);
        //consume(in, ")");
    } 
    
    return ir;
}

ParseTree::Designator parseDesignator(string &in)
{
    ParseTree::Designator designator;
    consume(in, "Designator");
    designator.identifier = new ParseTree::Identifier(parseIdentifier(in));
    designator.rest = parseList(in, parseIdentRest);
    return designator;
}

ParseTree::Assignment parseAssignment(string &in)
{
    ParseTree::Assignment assignment;
    consume(in, "Assignment");
    consume(in, "(");
    assignment.designator = parseDesignator(in);
    consume(in, ")");
    consume(in, "(");
    assignment.op = parseOperator(in);
    consume(in, ")");
    consume(in, "(");
    assignment.expression = new ParseTree::Expression(parseExpression(in));
    consume(in, ")");
    
    return assignment;
}

ParseTree::OpExpr parseOpExpr(string &in)
{
    ParseTree::OpExpr opExpr;
    
    if (optionalConsume(in, "OpExpr ")) 
    {
        opExpr.opExprType = ParseTree::OpExpr::Expr;
        consume(in, "(");
        opExpr.unary = new ParseTree::Unary(parseUnary(in));
        consume(in, ")");
        consume(in, "(");
        opExpr.op = new ParseTree::Operator(parseOperator(in));
        consume(in, ")");
        consume(in, "(");
        opExpr.next = new ParseTree::OpExpr(parseOpExpr(in));
        consume(in, ")");
    } else if (optionalConsume(in, "OpSingle ")) 
    {
        opExpr.opExprType = ParseTree::OpExpr::Single;
        consume(in, "(");
        opExpr.unary = new ParseTree::Unary(parseUnary(in));
        consume(in, ")");
    } else if (optionalConsume(in, "OpEmpty")) 
    {
        // should not happen
        opExpr.opExprType = ParseTree::OpExpr::Empty;
    }
    
    return opExpr;
}

ParseTree::Expression parseExpression(string &in)
{
    ParseTree::Expression expression;
    
    if (optionalConsume(in, "ExprOp ")) 
    {
        expression.exprType = ParseTree::Expression::ExprOp;
        consume(in, "(");
        expression.opExpr = new ParseTree::OpExpr(parseOpExpr(in));
        consume(in, ")");
    } else if (optionalConsume(in, "ExprAss ")) 
    {
        expression.exprType = ParseTree::Expression::ExprAss;
        consume(in, "(");
        expression.assignment = new ParseTree::Assignment(parseAssignment(in));
        consume(in, ")");
    } else {
        TException up(4, "Expected an expression!");
        throw up;
    }
    return expression; 
}

ParseTree::StatementExpression parseStatementExpression(string &in)
{
    ParseTree::StatementExpression se;
    if (optionalConsume(in, "StatDesignator ")) 
    {
        se.seType = ParseTree::StatementExpression::StatDesignator;
        consume(in, "(");
        se.designator = new ParseTree::Designator(parseDesignator(in));
        consume(in, ")");
        //se.params = parseList(in, parseExpression);
    } else if (optionalConsume(in, "StatExpr "))
    {
        se.seType = ParseTree::StatementExpression::StatExpression;
        consume(in, "(");
        se.expression = new ParseTree::Expression(parseExpression(in));
        consume(in, ")");
    } 
    return se;
}

ParseTree::Statement parseStatement(string &in)
{
    ParseTree::Statement statement;
    
    if (optionalConsume(in, "BlockStatement (Block ")) 
    {
        statement.statementType = ParseTree::Statement::Block;
        statement.block = parseList(in, parseStatement);
        consume(in, ")");
    } else if (optionalConsume(in, "DeclStatement ("))
    {
        statement.statementType = ParseTree::Statement::Declaration;
        statement.decl = new ParseTree::Declaration(parseDeclaration(in));
        consume(in, ")");
    } else if (optionalConsume(in, "ExprStatement ("))
    {
        statement.statementType = ParseTree::Statement::Expression;
        statement.expr = new ParseTree::StatementExpression(parseStatementExpression(in));
        consume(in, ")");
    } else if (optionalConsume(in, "ReturnStatement ("))
    {
        statement.statementType = ParseTree::Statement::Return;
        statement.returnexpr = new ParseTree::Expression(parseExpression(in));
        consume(in, ")");
    }
    
    return statement;
}

ParseTree::Var parseVar(string &in)
{
    ParseTree::Var var;
    if (optionalConsume(in, "Var ")) 
    {
        var.init = false;
        var.identifier = parseIdentifier(in);
    } else if (optionalConsume(in, "VarInit ")) 
    {
        var.init = true;
        var.identifier = parseIdentifier(in);
        consume(in, "(");
        var.expression = parseExpression(in);
        consume(in, ")");
    }
    
    return var;
}

ParseTree::ClassDeclaration parseClassDeclarations(string &in)
{
    ParseTree::ClassDeclaration decl;
    
    if (optionalConsume(in, "ConstructorDecl ")) 
    {
        // Constructor Declaration
        decl.declType = ParseTree::ClassDeclaration::Constructor;
        decl.modifiers = parseList(in, parseModifier);
        decl.identifier = parseIdentifier(in);
        decl.formPars = parseList(in, parseFormPar);
        consume(in, "(");
        decl.statement = parseStatement(in);
        consume(in, ")");
    } else if (optionalConsume(in, "FieldMethDecl "))
    {
        decl.declType = ParseTree::ClassDeclaration::FieldMeth;
        consume(in, "(");
        decl.declaration = new ParseTree::Declaration(parseDeclaration(in));
        consume(in, ")");
    }
    
    return decl;
}

ParseTree::Declaration parseDeclaration(string &in)
{
    ParseTree::Declaration decl;
    if (optionalConsume(in, "FunctionDecl ")) 
    {
        // Function Declaration
        decl.declType = ParseTree::Declaration::FunctionDecl;
        consume(in, "(FunctionHeader ");
        decl.modifiers = parseList(in, parseModifier);
        consume(in, "(");
        decl.type = parseType(in);
        consume(in, ")");
        decl.identifier = parseIdentifier(in);
        decl.formPars = parseList(in, parseFormPar);
        consume(in, ")");
        consume(in, "(");
        decl.statement = parseStatement(in);
        consume(in, ")");
    } else if (optionalConsume(in, "VarDecl "))
    {
        decl.declType = ParseTree::Declaration::VarDecl;
        decl.modifiers = parseList(in, parseModifier);
        consume(in, "(");
        decl.type = parseType(in);
        consume(in, ")");
        decl.vars = parseList(in, parseVar);
    } else if (optionalConsume(in, "ClassDecl "))
    {
        decl.declType = ParseTree::Declaration::ClassDecl;
        decl.modifiers = parseList(in, parseModifier);
        decl.identifier = parseIdentifier(in);
        decl.extends = parseList(in, parseName);
        decl.classdecls = parseList(in, parseClassDeclarations);
    } else {
        TException up(2, "Expected a declaration!");
        throw up;
    }
    
    return decl;
}

ParseTree::Type parseType(string &in)
{
    ParseTree::Type type;
    
    consume(in, "Type (");
    
    if (optionalConsume(in, "PrimitiveType")) 
    {
        type.typeType = ParseTree::Type::PrimitiveType;
        if (optionalConsume(in, "PrimInt"))
            type.primitiveType = ParseTree::Type::Int; 
        if (optionalConsume(in, "PrimFloat"))
            type.primitiveType = ParseTree::Type::Float; 
        if (optionalConsume(in, "PrimBool"))
            type.primitiveType = ParseTree::Type::Bool; 
        if (optionalConsume(in, "PrimChar"))
            type.primitiveType = ParseTree::Type::Char; 
        if (optionalConsume(in, "PrimShort"))
            type.primitiveType = ParseTree::Type::Short; 
        if (optionalConsume(in, "PrimLong"))
            type.primitiveType = ParseTree::Type::Long; 
        if (optionalConsume(in, "PrimDouble"))
            type.primitiveType = ParseTree::Type::Double; 
        if (optionalConsume(in, "PrimByte"))
            type.primitiveType = ParseTree::Type::Byte;
    } else {
        // UserType
        consume(in, "UserType");
        type.typeType = ParseTree::Type::UserType;
        type.userType = parseName(in);
    }
    
    consume(in, ")");
    type.arrayExpr = parseList(in, parseExpression);
    //consume(in, ")");
    return type;
}

ParseTree::Thinder parseThinder(string &in)
{
    ParseTree::Thinder thinder;
    consume(in, "Thinder ");
    if (optionalConsume(in, "Nothing "))
    {
        //thinder.ns = ;
    }
    else 
    {
        consume(in, "(Just ");
        thinder.ns = parseName(in);
        consume(in, ")");
    }
    thinder.imports = parseList(in, parseName);
    //consume(in, " ");
    thinder.declarations = parseList(in, parseDeclaration);
    
    return thinder;
}

} // namespace Thinder
